﻿using Unity.Mathematics;
using System.Runtime.CompilerServices;

namespace GameBasic
{
    /// <summary>
    /// A Dispersed-Timer record time as integer step and frame
    /// </summary>
    public struct StepTimer
    {
        /// <summary>
        /// DeltaTime = Step * StepToTime
        /// </summary>
        public const float StepToTime = 0.005f;

        /// <summary>
        /// Step = DeltaTime * TimeToStep
        /// </summary>
        public const float TimeToStep = 200;

        /// <summary>
        /// Default minimum time to trigger
        /// </summary>
        public const float DefaultMinHoldTime = 0.33f;
        /// <summary>
        /// Default minimum step to trigger
        /// </summary>
        public const byte DefaultMinHoldStep = (byte)(DefaultMinHoldTime * TimeToStep);
        /// <summary>
        /// Default minimum frame to trigger
        /// </summary>
        public const byte DefaultMinHoldFrame = 8;

        public static readonly StepTimer Default = new StepTimer()
        {
            minTime = DefaultMinHoldStep,
            minFrame = DefaultMinHoldFrame
        };

        /// <summary>
        /// Current time step
        /// </summary>
        public byte timeStep;

        /// <summary>
        /// Current frame
        /// </summary>
        public byte frame;

        /// <summary>
        /// Minimum time step to trigger
        /// </summary>
        public byte minTime;

        /// <summary>
        /// Minimum frame to trigger
        /// </summary>
        public byte minFrame;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool Update(int deltaTimeStep)
        {
            int2 data = math.min(new int2(deltaTimeStep + timeStep, frame + 1), 255);
            timeStep = (byte)(data.x);
            frame = (byte)(data.y);

            return data.x >= minTime & data.y >= minFrame;
        }

        public bool UpdateCeil(float deltaTime)
        {
            return Update(ToStepCeil(deltaTime));
        }

        /// <summary>
        /// Reset this timer, 
        /// </summary>
        /// <param name="reset"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Reset(int reset)
        {
            reset = reset - 1;
            timeStep = (byte)(timeStep & reset);
            frame = (byte)(frame & reset);
        }

        /// <summary>
        /// Convert delta time to time step
        /// </summary>
        /// <param name="deltaTime"></param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static float ToStep(float deltaTime)
        {
            return deltaTime * TimeToStep;
        }

        /// <summary>
        /// Convert deltaTime to time step
        /// </summary>
        /// <param name="deltaTime"></param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int ToStepCeil(float deltaTime)
        {
            return (int)math.ceil(deltaTime * TimeToStep);
        }

        /// <summary>
        /// Convert timestep to deltatime
        /// </summary>
        /// <param name="timeStep"></param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static float ToTime(int timeStep)
        {
            return timeStep * StepToTime;
        }
    }
}